using SmartCommunity.Models;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace SmartCommunity.Services.Community
{
    /// <summary>
    /// 服务类：
    /// </summary>
    public class BuildingService : BaseService<BuildingEntity>
    {
        #region 构造函数
        protected readonly SCDBContext ctx;
        public BuildingService(SCDBContext ctx)
        {
            this.ctx = ctx;
        }
        #endregion

        #region 数据查询 
        /// <summary>
        /// 分页查询
        /// </summary>
        public async Task<PageResult<BuildingDto>> FindPagedList(BuildingQuery buildingQuery, Pager pager, LoginUser user)
        {
            var query = ctx.Buildings.Include(a => a.Area).AsNoTracking().Where(buildingQuery.GetQueryExpression());
            var data = await FindPagedList(query, pager, o => o.OrderByDescending(t => t.CreatedTime));
            var pageDto = data.Map(o => o.Adapt<BuildingDto>());
            var dicts = await ctx.Dicts.Include(a => a.DictItems).Where(a => a.DictType.StartsWith("Building")).ToListAsync();
            var map = dicts.ToDictionary(k => k.DictType, v => v.DictItems.ToDictionary(k => k.DictValue, v => v.DictLabel));
            await MergeEnumName(pageDto.DataList);
            return pageDto;
        }

        public async Task<IEnumerable<BuildingDto>> GetBuildingByAreaId(Guid areaId)
        {
            var lst = await ctx.Buildings.Where(a => a.AreaId == areaId && a.BuildingLevel == 1).ToListAsync();
            return lst.Select(a => a.Adapt<BuildingDto>());
        }

        /// <summary>
        /// 根据ID查询
        /// </summary>
        public async Task<BuildingDto> FindBuildingById(Guid buildingId)
        {
            var entity = await FindById(buildingId);
            return entity.Adapt<BuildingDto>();
        }

        /// <summary>
        /// 根据ID查询实体类
        /// </summary>
        private async Task<BuildingEntity> FindById(Guid id)
        {
            return await ctx.Buildings.SingleOrDefaultAsync(a => a.BuildingId == id)
                ?? throw new NotFoundException($"找不到指定的,Id:{id}");
        }

        public async Task<IEnumerable<SelectOption>> GetAreaTree()
        {
            var buildings = await ctx.Communities.Include(a => a.Areas).ToListAsync();
            var nodes = new List<SelectOption>();
            foreach (var community in buildings)
            {
                var node = new SelectOption(community.CommunityId,community.CommunityName) {  Icon = "mdi-city" };
                if (community.Areas.Count > 0)
                {
                    foreach (var area in community.Areas)
                    {
                        var areaNode = new SelectOption(area.AreaId, area.AreaName) { Icon = "mdi-home-city" };
                        node.Children.Add(areaNode);
                    }
                }
                nodes.Add(node);
            }
            return nodes;
        }

        public async Task<IEnumerable<BuildingDto>> GetBuildingTree()
        {
            var buildings = await ctx.Communities.Include(a => a.Areas).ThenInclude(t => t.Buildings).ToListAsync();
            var dtos = buildings.SelectMany(a => a.Areas.SelectMany(t => t.Buildings.Select(b => b.Adapt<BuildingDto>()))).ToList();
            await MergeEnumName(dtos);

            var nodes = new List<BuildingDto>();
            foreach (var community in buildings)
            {
                var node = new BuildingDto() { BuildingId = community.CommunityId, CommunityId=community.CommunityId, BuildingName = community.CommunityName, BuildingLevel = -1, Icon = "mdi-city" };
                if (community.Areas.Count > 0)
                {
                    foreach (var area in community.Areas)
                    {
                        var areaNode = new BuildingDto() { BuildingId = area.AreaId, CommunityId = community.CommunityId,AreaId=area.AreaId, BuildingName = area.AreaName, BuildingLevel = 0, Icon = "mdi-home-city" };
                        var areaBuilds = dtos.Where(a => a.AreaId == area.AreaId).ToList();
                        if (areaBuilds.Count > 0)
                        {
                            areaNode.Children.AddRange(ConvertTree(areaBuilds, Guid.Empty, true));
                        }
                        node.Children.Add(areaNode);
                    }
                }
                nodes.Add(node);
            }
            return nodes;
        }
        private List<BuildingDto> ConvertTree(IEnumerable<BuildingDto> buildings, Guid parentId, bool topLevel)
        {
            var icon = topLevel ? "mdi-office-building" : "mdi-office-building-outline";
            var nodes = buildings.Where(a => (a.ParentId ?? Guid.Empty) == parentId).ToList();
            foreach (var n in nodes)
            {
                n.Icon = icon;
                n.Children = ConvertTree(buildings, n.BuildingId, false);
            }
            return nodes;
        }

        public async Task<IEnumerable<BuildingDto>> GetBuildingTrees()
        {
            var buildings = await ctx.Buildings.Include(a => a.Area).ToListAsync();
            var dicts = await ctx.Dicts.Include(a => a.DictItems).Where(a => a.DictType.StartsWith("Building")).ToListAsync();
            var map = dicts.ToDictionary(k => k.DictType, v => v.DictItems.ToDictionary(k => k.DictValue, v => v.DictLabel));
            var dtos = buildings.Where(a => a.Parent == null)
                .Select(a => a.Adapt<BuildingDto>()).OrderBy(a => a.AreaId).ThenBy(a => a.BuildingNo)
                .ToList();
            foreach (var d in dtos)
            {
                if (map.TryGetValue("BuildingLevel", out var level) && level.TryGetValue(d.BuildingLevel.ToString(), out var ln)) d.BuildingLevelName = ln;
                if (map.TryGetValue("BuildingType", out var type) && type.TryGetValue(d.BuildingType.ToString(), out var tn)) d.BuildingTypeName = tn;
                if (map.TryGetValue("BuildingUsage", out var usage) && usage.TryGetValue(d.BuildingUsage.ToString(), out var un)) d.BuildingUsageName = un;
                if (map.TryGetValue("BuildingDirection", out var dir) && dir.TryGetValue(d.BuildingDirection.ToString(), out var dn)) d.BuildingDirectionName = dn;
                foreach (var di in d.Children)
                {

                    if (map.TryGetValue("BuildingLevel", out var ilevel) && ilevel.TryGetValue(di.BuildingLevel.ToString(), out var iln)) di.BuildingLevelName = iln;
                    if (map.TryGetValue("BuildingType", out var itype) && itype.TryGetValue(di.BuildingType.ToString(), out var itn)) di.BuildingTypeName = itn;
                    if (map.TryGetValue("BuildingUsage", out var iusage) && iusage.TryGetValue(di.BuildingUsage.ToString(), out var iun)) di.BuildingUsageName = iun;
                    if (map.TryGetValue("BuildingDirection", out var idir) && idir.TryGetValue(di.BuildingDirection.ToString(), out var idn)) di.BuildingDirectionName = idn;

                }
            }
            return dtos;
        }

        private async Task MergeEnumName(IEnumerable<BuildingDto> dtos)
        {
            var dicts = await ctx.Dicts.Include(a => a.DictItems).Where(a => a.DictType.StartsWith("Building")).ToListAsync();
            var levelMap = dicts.SingleOrDefault(a => a.DictType == "BuildingLevel")!.DictItems.ToDictionary(k => k.DictValue, v => v.DictLabel);
            var typeMap = dicts.SingleOrDefault(a => a.DictType == "BuildingType")!.DictItems.ToDictionary(k => k.DictValue, v => v.DictLabel);
            var usageMap = dicts.SingleOrDefault(a => a.DictType == "BuildingUsage")!.DictItems.ToDictionary(k => k.DictValue, v => v.DictLabel);
            var dirMap = dicts.SingleOrDefault(a => a.DictType == "BuildingDirection")!.DictItems.ToDictionary(k => k.DictValue, v => v.DictLabel);
            foreach (var d in dtos)
            {
                if (levelMap != null && levelMap.TryGetValue(d.BuildingLevel.ToString(), out var ln)) d.BuildingLevelName = ln;
                if (typeMap != null && typeMap.TryGetValue(d.BuildingType.ToString(), out var tn)) d.BuildingTypeName = tn;
                if (usageMap != null && usageMap.TryGetValue(d.BuildingUsage.ToString(), out var un)) d.BuildingUsageName = un;
                if (dirMap != null && dirMap.TryGetValue(d.BuildingDirection.ToString(), out var dn)) d.BuildingDirectionName = dn;
            }
        }
        #endregion

        #region 增 删 改
        /// <summary>
        /// 新增
        /// </summary>
        public async Task<BuildingDto> CreateBuilding(BuildingDto dto, LoginUser user)
        {
            // 新建对象
            var entity = new BuildingEntity();
            // 复制属性
            await CopyDtoToEntity(dto, entity, user, true);
            // 执行保存
            ctx.Buildings.Add(entity);
            await ctx.SaveChangesAsync();
            // 返回Dto
            return entity.Adapt<BuildingDto>();
        }

        /// <summary>
        /// 更新
        /// </summary>
        public async Task UpdateBuilding(BuildingDto dto, LoginUser user)
        {
            // 原始数据
            var entity = await FindById(dto.BuildingId);
            // 更新字段
            await CopyDtoToEntity(dto, entity, user, false);
            // 执行更新
            await ctx.SaveChangesAsync();
        }

        /// <summary>
        /// 复制dto属性到实体字段
        /// </summary>
        private async Task CopyDtoToEntity(BuildingDto dto, BuildingEntity entity, LoginUser user, bool isNew)
        {
            if (dto.BuildingLevel > 1 && dto.ParentId == null)
            {
                throw new MessageException("单元楼必须选择对应楼栋");
            }
            // 检查数据重复
            if (await ctx.Buildings.AnyAsync(a => a.BuildingId != dto.BuildingId && (a.ParentId == dto.ParentId && a.BuildingNo == dto.BuildingNo)))
            {
                throw new MessageException("当前已存在相同名称或编码的");
            }

            entity.AreaId = dto.AreaId;
            entity.ParentId = dto.ParentId;
            entity.BuildingNo = dto.BuildingNo;
            entity.BuildingName = dto.BuildingName;
            entity.BuildingLevel = dto.BuildingLevel;
            entity.BuildingUsage = dto.BuildingUsage;
            entity.BuildingType = dto.BuildingType;
            entity.BuildingDirection = dto.BuildingDirection;
            entity.Floor = dto.Floor;
            entity.Remark = dto.Remark;

            // 记录操作人
            if (isNew)
            {
                entity.CreatedBy = user.Username;
                entity.CreatedTime = DateTime.Now;
            }
            entity.UpdatedBy = user.Username;
            entity.UpdatedTime = DateTime.Now;
        }

        /// <summary>
        /// 根据ID删除
        /// </summary>
        public async Task DeleteByIds(IEnumerable<Guid> ids, LoginUser user)
        {
            var buildings = await ctx.Buildings.Where(a => ids.Contains(a.BuildingId)).ToListAsync();
            if (buildings.Count != ids.Count())
            {
                throw new MessageException("查询出的数据和传入的ID不匹配，请刷新后再试。");
            }
            //TODO 判断是否能删除
            foreach (var entity in buildings)
            {
                ctx.Buildings.Remove(entity);
            }
            // 提交事务
            await ctx.SaveChangesAsync();
        }



        #endregion
    }
}
